home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 3
/
Info_Mac_1994-01.iso
/
Development
/
Information
/
Mac Programming Secrets 1.0.1
/
Chapter 08
/
Neat Stuff.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-19
|
11KB
|
348 lines
#include "Neat Stuff.h"
#include "Script.h"
#define NIL 0L
const short kData = 1;
const short kResource = 2;
const short kGetDirectoryDLOGID = 6042;
const short kDirectorySelectButton = 10;
Ptr gBufferPtr = NIL;
long gAmountInBuffer = 0;
const long kCopyBufferSize = 20 * 1024;
/*******************************************************************************
CopyAFile
Queries the user for a single file and a destination for a copy of that
file. The file is then copied to that new location. The copy is performed
in small chunks that will handle any file, no matter how large. First, the
data fork is copied. If that goes OK, the resource fork is copied. If
there is no error, the directory information for that file (including the
Finder information) is transferred. However, any Finder comments
associated with the file are not transferred as they are kept in the
Finder’s private data file.
*******************************************************************************/
OSErr CopyAFile()
{
OSErr err;
FSSpec source;
FSSpec destination;
//
// Get the name of a file to copy, and a destination for the copy.
//
if (!GetAFile(&source))
return userCanceledErr;
if (!GetADirectory(&destination))
return userCanceledErr;
//
// If the destination is the same as the source, do nothing.
//
if ((source.vRefNum == destination.vRefNum) && (source.parID == destination.parID))
return noErr;
BlockMove(&source.name, &destination.name, source.name[0] + 1);
//
// Get a little buffer for reading and writing. Abort if there
// are any errors.
//
gBufferPtr = NewPtr(kCopyBufferSize);
if (MemError() != noErr) return MemError();
if (gBufferPtr == NIL) return memFullErr;
//
// Copy the data and resource forks. Transfer the directory information.
//
err = CopyFork(&source, &destination, kData);
if (err == noErr)
err = CopyFork(&source, &destination, kResource);
if (err == noErr)
err = TransferCatInfo(&source, &destination);
if (err != noErr)
err = FSpDelete(&destination);
DisposePtr(gBufferPtr);
return err;
}
/*******************************************************************************
CopyFork
Given a source file and a destination specification, copy the fork
designated by “whichFork.” Open the source and destination files, creating
the destination if necessary. Set the size of the destination fork to the
size of the source fork (we can quickly catch any “Out of disk space”
errors this way). Then copy the fork in small chunks: read a little, then
write a little until the whole fork is copied. Close both files when we’re
done. Leave it up to the caller to delete the destination on any errors.
*******************************************************************************/
OSErr CopyFork(FSSpec* source, FSSpec* destination, short whichFork)
{
OSErr err;
Boolean done;
short sourceRefNum;
short destinationRefNum;
err = OpenFiles(source, destination, &sourceRefNum, &destinationRefNum, whichFork);
if (err != noErr) return err;
err = SetDestinationFileSize(sourceRefNum, destinationRefNum);
if (err != noErr) return err;
done = FALSE;
do {
gAmountInBuffer = kCopyBufferSize;
err = FSRead(sourceRefNum, &gAmountInBuffer, gBufferPtr);
if (err == eofErr) {
err = noErr;
done = TRUE;
}
if (err == noErr)
err = FSWrite(destinationRefNum, &gAmountInBuffer, gBufferPtr);
} while (!done && (err == noErr));
FSClose(sourceRefNum);
FSClose(destinationRefNum);
return err;
}
/*******************************************************************************
OpenFiles
Given FSSpecs for two files, open the designated fork for both files. The
source is assumed to exist. If the destination does not exist, it is
created. The refNums for both open files are returned. If there are any
errors, the refNums are not valid, and both files are closed.
The code for opening the resource fork of a file is almost identical to
the code for opening a data fork. Therefore, at the start of the function,
we set a procedure pointer to either a routine that opens the data fork or
a routine that opens a resource fork, whichever is appropriate. Whatever
routine is pointed to is called in the heart of the logic of the rest of
the function as needed.
*******************************************************************************/
OSErr OpenFiles(FSSpec* source, FSSpec* destination,
short* sourceRefNum, short* destRefNum, short whichFork)
{
typedef OSErr (*OpenProcPtr) (const FSSpec *spec, char permission, short *refNum);
OpenProcPtr openProc;
OSErr err;
if (whichFork == kData) {
openProc = DoOpenDF;
} else {
openProc = DoOpenRF;
}
err = openProc(source, fsRdPerm, sourceRefNum);
if (err != noErr)
return err;
err = openProc(destination, fsRdWrPerm, destRefNum);
if (err == fnfErr) {
err = FSpCreate(destination, 'Fox ', 'Trot', smSystemScript);
if (err == noErr)
err = openProc(destination, fsRdWrPerm, destRefNum);
}
if (err != noErr)
FSClose(*sourceRefNum);
return err;
}
//
// Wrappers for FSpOpenDF and FSpOpenRF. We genericize the above routine by
// using a pointer to a function that opens either a data fork or a resource
// fork. However, FSpOpenDF and FSpOpenRF implemented with inline instructions,
// so there is no function that we can point to. Neither can we use
// GetTrapAddress to get the address of these routines; they share a single
// trap, and, hence, jump to the same address. The system knows which function
// you really want by a “selector” value that is pushed on the stack by the
// the inline instructions.
//
OSErr DoOpenDF(const FSSpec *spec, char permission, short *refNum)
{
return FSpOpenDF( (FSSpec*) spec, permission, refNum);
}
OSErr DoOpenRF(const FSSpec *spec, char permission, short *refNum)
{
return FSpOpenRF( (FSSpec*) spec, permission, refNum);
}
/*******************************************************************************
TransferCatInfo
Transfer the directory information of the source file to the destination
file. Call PBGetCatInfo on the source file. If that succeeds, call
PBSetCatInfo on the destination.
*******************************************************************************/
OSErr TransferCatInfo(FSSpec* source, FSSpec* destination)
{
CInfoPBRec pb;
OSErr err;
pb.hFileInfo.ioVRefNum = source->vRefNum;
pb.hFileInfo.ioNamePtr = source->name;
pb.hFileInfo.ioFDirIndex = 0;
pb.hFileInfo.ioDirID = source->parID;
err = PBGetCatInfoSync(&pb);
if (err == noErr) {
pb.hFileInfo.ioVRefNum = destination->vRefNum;
pb.hFileInfo.ioNamePtr = destination->name;
pb.hFileInfo.ioFDirIndex = 0;
pb.hFileInfo.ioDirID = destination->parID;
err = PBSetCatInfoSync(&pb);
}
return err;
}
/*******************************************************************************
SetDestinationFileSize
Given the refNums to two open file forks, set the size of the destination
to be the same as the source. Call GetEOF on the source file. If that
succeeds, we use the result in a call to SetEOF for the destination file.
*******************************************************************************/
OSErr SetDestinationFileSize(short sourceRefNum, short destinationRefNum)
{
OSErr err;
long forkSize;
err = GetEOF(sourceRefNum, &forkSize);
if (err == noErr)
err = SetEOF(destinationRefNum, forkSize);
return err;
}
/*******************************************************************************
GetAFile
Get the FSSpec of a file to open. Call StandardGetFile to allow the user
to select any file. We pass “-1” for the numTypes parameter to tell
Standard File to display all files. Copy the FSSpec of the selected file
to the FSSpec supplied by the caller. Return a Boolean indicating whether
or not the user pressed Cancel. If the user _did_ press Cancel, the FSSpec
is not valid.
*******************************************************************************/
Boolean GetAFile(FSSpec* file)
{
SFTypeList types; // dummy - not used
StandardFileReply reply;
StandardGetFile(NIL, // FileFilterProcPtr fileFilter
-1, // short numTypes
types, // SFTypeList typeList
&reply); // StandardFileReply *reply
if (reply.sfGood)
BlockMove(&reply.sfFile, file, sizeof(FSSpec));
return reply.sfGood;
}
/*******************************************************************************
GetADirectory
Return a specification for the directory where the file should be copied.
Call CustomGetFile to allow the user to select a directory. If the user
selects a directory, the vRefNum and dirID are copied into the caller’s
FSSpec; the “name” field is not used. A Boolean is returned indicating
whether or not the user selected Cancel. If the user _did_ press Cancel,
the FSSpec is not valid.
The interface for selecting a directory is similar to the standard GetFile
dialog box. Two items have been added: a “Select” button and a line of
help text. The idea is that the user moves into the destination directory
and then presses the Select button.
To implement the “Select a directory” dialog box, we need to use a custom
file filter and a custom dialog hook. The file filter is used to display
folders only, and to filter out any files. The dialog hook is used to
handle clicks on the Select button. If the user clicks on that button, we
dismiss the dialog by faking Standard File into thinking the Cancel button
was clicked. However, we use the “yourDataPtr” parameter in CustomGetFile
to pass a pointer to a Boolean. We use this Boolean to keep track of
whether Cancel was really clicked on, or if we are just faking a click on
Cancel. When CustomGetFile returns, we look at the Boolean to decide
whether a directory was selected or not.
*******************************************************************************/
Boolean GetADirectory(FSSpec* directory)
{
SFTypeList types;
StandardFileReply reply;
Boolean reallyPressedSelect = FALSE;
Point where = {-1, -1}; // center it on the main screen
CustomGetFile( (FileFilterYDProcPtr) GetDirFileFilter,
-1, // short numTypes,
types, // SFTypeList typeList,
&reply, // StandardFileReply *reply,
kGetDirectoryDLOGID, // short dlgID,
where, // Point where,
(DlgHookYDProcPtr) GetDirDlgHook,
NIL, // ModalFilterYDProcPtr filterProc,
NIL, // short *activeList,
NIL, // ActivateYDProcPtr activateProc,
(void*) &reallyPressedSelect); // void *yourDataPtr);
if (reallyPressedSelect)
BlockMove(&reply.sfFile, directory, sizeof(FSSpec));
return reallyPressedSelect;
}
pascal Boolean GetDirFileFilter(ParmBlkPtr PB, StandardFileReply *replyPtr)
{
if ((PB->fileParam.ioFlAttrib & ioDirMask) != 0)
return FALSE; // It’s a directory; show it
else
return TRUE; // It’s a file; don’t show it
}
pascal short GetDirDlgHook(short item, DialogPtr theDialog,
Boolean *reallyPressedSelect)
{
if (item == kDirectorySelectButton) {
*reallyPressedSelect = TRUE; // Keep track of what really happened
item = sfItemCancelButton; // Fake a click on the Cancel button
}
return item;
}